package com.ipemon.server;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;

import com.ipemon.exception.DataNotSentException;
import com.ipemon.exception.GameProtocolDisRespectException;
import com.ipemon.model.IPemon;
import com.ipemon.model.Logger;
import com.ipemon.model.Player;

public class PlayerNet implements CallBack, Runnable{
	private PseudoTCPoverUDP pseudoTCP = null;
	private Player player = null;
	
	private String player2name = "";
	private int player2rapidite = 0;
	private IPemon player2ipemon = null;
	
	private CallBack callback = null;
	
	private boolean dataIn = false;
	private List<String> data = null;

	private Thread playerThread = null;
	
	private boolean replyForDuelInitReceived = false;
	
	public PlayerNet(Player player, CallBack callback) throws IOException {
		this.player = player;
		this.callback = callback;
		playerThread = new Thread(this, "PlayerThread");
		playerThread.start();

		pseudoTCP = PseudoTCPoverUDP.getFirstInstance();
		pseudoTCP.receiveData(this);
	}
	
	public PlayerNet(PseudoTCPoverUDP pseudoTCP, CallBack callback) throws IOException {
		this.player = new Player("player");
		this.callback = callback;
		playerThread = new Thread(this, "PlayerThread");
		playerThread.start();
		
		this.pseudoTCP = pseudoTCP;
		this.pseudoTCP.receiveData(this);
	}
	
	
	
	public Player getPlayer() {
		return player;
	}

	public void setPlayer(Player player) {
		this.player = player;
	}

	public String getPlayer2name() {
		return player2name;
	}

	public int getPlayer2rapidite() {
		return player2rapidite;
	}

	public synchronized IPemon getIPemonFromForet(String foretAddress, 
			int portNumber) throws GameProtocolDisRespectException, UnknownHostException {
		
		String strGetNewIPemon = "NOUVEAU " + player.getIdentifiant();
		
		try {
			
			Logger.getInstance().log(this, "Sending request to foret");
			
			pseudoTCP.sendData(InetAddress.getByName(foretAddress), portNumber, strGetNewIPemon);
			pseudoTCP.receiveData(this);
			Logger.getInstance().log(this, "Successfully sent the new ipemon request to foret");
			
			while(!Semaphores.ifreplyReceivedForNouveauIPemon()){
				Thread.currentThread().sleep(1000);
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (DataNotSentException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		Logger.getInstance().log(this, "Foret replies with an ipemon");
		String protocol[] = Semaphores.getData().split(" ");

		IPemon ipemon;
		
		if(protocol[0].equalsIgnoreCase("donne")) {
			String newipemonString = protocol[1];
			
			ipemon = IPemon.getIPemonByDataStream(newipemonString);
			
		} else {
			throw new GameProtocolDisRespectException(this, "NOUVEAU " + player.getIdentifiant());
		}

		Semaphores.setreplyReceivedForNouveauIPemon(false);
		
		return ipemon;
	}
	
	public synchronized boolean connectToAnotherDresseur(String playerAddress, 
			int portNumber) throws GameProtocolDisRespectException, UnknownHostException {
		
		String strStartChallenge = "DUEL_INIT " + player.getIdentifiant() + " " + player.getRapidite();
		
		try {
			pseudoTCP.sendData(InetAddress.getByName(playerAddress), portNumber, strStartChallenge);
			
			replyForDuelInitReceived = false;
			
			while(!replyForDuelInitReceived)
				Thread.currentThread().sleep(1000);
			
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		} catch (DataNotSentException e) {
			e.printStackTrace();
			return false;
		} catch (InterruptedException e) {
			e.printStackTrace();
			return false;
		}
		
		return true;
	}
	
	public void replyCombatRequest(boolean accept) {
		
		if(accept)	{
			String replyData = "DUEL_OK " + 	player.getIdentifiant() +
									" " + player.getDefaultIPemon().getRapidite();
			
			try {
				
				pseudoTCP.sendData(replyData);
				
			} catch (IOException | DataNotSentException e) {
				replyCombatRequest(accept);
				e.printStackTrace();
			}
			
		}
		
	}
	

	public void startCombat() {

		if(getPlayer2rapidite() < player.getRapidite()) {
			attack();
		}
		
		else {
			defence();
		}

		
	}
	
	public void defence() {
		player.setCombatRole(Player.DEFENCE);
		callback.callIfReceived(CallBack.DEFENSE, null);
		combat(player.getDefaultIPemon(), player2ipemon);
		
	}
	
	public void attack() {
		
		
		try {
			player.setCombatRole(Player.ATTACK);
			callback.callIfReceived(CallBack.ATTAQUE, null);
			
			String attaque = "ATTAQUE " + player.getDefaultIPemon().getIPemonStream();
			pseudoTCP.sendData(attaque);
		
		} catch (IOException e) {
			e.printStackTrace();
		} catch (DataNotSentException e) {
			e.printStackTrace();
		}
	}
	
	public void combat(IPemon player, IPemon player2) {
		String issue = "ISSUE 2:";
		if(player.getVieCur() > 0) {
			issue += ":OK";
		} else {
			issue +=":KO";
		}
		
		try {
			pseudoTCP.sendData(issue);
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (DataNotSentException e) {
			e.printStackTrace();
		}
		
	}
	
	private synchronized void listen() {
		
		while(!dataIn) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Logger.getInstance().log(this, "PlayerThread notified");
			
		try {
			treatListenedData();
		} catch (GameProtocolDisRespectException e) {
			e.printStackTrace();
		}
		
		listen();
	}

	public void treatListenedData() throws GameProtocolDisRespectException {
		
		String protocolStr[] = data.get(0).split(" ");
		
		if(protocolStr[0].contains("DONNE")) {
			
			Semaphores.setData(data.get(0));
			Semaphores.setreplyReceivedForNouveauIPemon(true);
			
		}
		
		else if(protocolStr[0].contains("DUEL_INIT")) {
			
			if(protocolStr.length == 3) {
				
				try {
					player2rapidite = Integer.valueOf(protocolStr[2].replaceAll("[^0-9]+", ""));
					player2name = protocolStr[1];
					
					new Thread(new Runnable() {
						@Override
						public void run() {
							callback.callIfReceived(CallBack.COMBAT, player2name);
						}
					}).start();
					
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
				
			} else {
				throw new GameProtocolDisRespectException(data.get(0));
				
			}
		} 
		
		else if (protocolStr[0].contains("DUEL_OK")) {
			
			if(protocolStr.length == 3) {
				try {
					player2rapidite = Integer.valueOf(protocolStr[2].replaceAll("[^0-9]+", ""));
					player2name = protocolStr[1];
					replyForDuelInitReceived = true;
					
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			} else {
				throw new GameProtocolDisRespectException(data.get(0));
			}

		} 
		
		else if (protocolStr[0].contains("ATTAQUE")) {
			
			if(protocolStr.length == 2) {
				
				player2ipemon = IPemon.getIPemonByDataStream(protocolStr[1]);
				
				defence();
			}
			
			else {
				throw new GameProtocolDisRespectException(data.get(0));
			}
			
		}
		
		else if (protocolStr[0].contains("ISSUE")) {
			
			if(protocolStr.length == 2) {
				
				String stringIssue[] = protocolStr[1].split(":");

				if(stringIssue.length == 2) {
					int lifeLost = Integer.valueOf(stringIssue[0].replaceAll("[^0-9]+", ""));
					String okko = stringIssue[1];
					
					player.getDefaultIPemon().setVieCur(player.getDefaultIPemon().getVieCur() - lifeLost);
					
					if(okko.contains("OK") ) {
						attack();
					}
					
					else if(okko.contains("KO")) {
						
					}
					
				}
				else 
					throw new GameProtocolDisRespectException(data.get(0));
				
			}
			
			else {
				throw new GameProtocolDisRespectException(data.get(0));
			}			
		}
		
		else {
			throw new GameProtocolDisRespectException(this, protocolStr[0]);
		}
		
		dataIn = false;
		data = null;
	}
	
	
	@Override
	public void callIfReceived(int type, Object data) {
		
		this.data = (List<String>) data;
		dataIn = true;
		
		try {
			treatListenedData();
		} catch (GameProtocolDisRespectException e) {
			e.printStackTrace();
		}
		
		Logger.getInstance().log(this, "All notified waiting at PlayerNet");
		return;
	}
	
	@Override
	public void run() {
		listen();
	}

	
	public static void main(String[] args) throws IOException{
		
		Logger.getInstance().setStack(true);
		
		PlayerNet test = new PlayerNet(new Player("player"), null);
		
		try {
			
		IPemon ipemon = test.getIPemonFromForet("localhost", 9000);
			System.out.println(ipemon);
			
			System.exit(0);
			
		} catch (GameProtocolDisRespectException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			System.out.println("output sud be out");
		}

	}



	
	

}

class Semaphores{
	private static boolean replyForNouveauIPemonReceived = false;
	private static String data = null;
	
	public static void setData(String data) {
		Semaphores.data = data;
	}
	
	public static String getData(){
		return Semaphores.data;
	}
	
	public static boolean ifreplyReceivedForNouveauIPemon(){
		return replyForNouveauIPemonReceived;
	}
	
	public static void setreplyReceivedForNouveauIPemon(boolean reply){
		replyForNouveauIPemonReceived = reply;
	}
}
